﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace NMF.Expressions
{
    internal abstract class ObservableExpressionBase : Expression
    {
        public override Expression Reduce()
        {
            return BaseReduce();
        }

        protected virtual Expression BaseReduce()
        {
            return base.Reduce();
        }
    }

    internal abstract class ObservableExpression<T> : ObservableExpressionBase, IObservableExpression<T>
    {
        protected ObservableExpression() { }

        protected ObservableExpression(T value)
        {
            this.value = value;
        }

        private T value;

        public T Value
        {
            get
            {
                return value;
            }
        }

        public sealed override Type Type
        {
            get
            {
                return typeof(T);
            }
        }

        public event EventHandler<ValueChangedEventArgs> ValueChanged;

        public virtual void Refresh()
        {
            var newVal = GetValue();
            if (!EqualityComparer<T>.Default.Equals(value, newVal))
            {
                var oldVal = value;
                value = newVal;
                OnValueChanged(oldVal, newVal);
            }
        }

        public override bool CanReduce
        {
            get
            {
                return CanBeConstant;
            }
        }

        public new virtual IObservableExpression<T> Reduce()
        {
            Attach();
            if (CanBeConstant)
            {
                return new ObservableConstant<T>(Value);
            }
            else
            {
                return this;
            }
        }

        protected override Expression BaseReduce()
        {
            Attach();
            if (CanBeConstant)
            {
                return new ObservableConstant<T>(Value);
            }
            else
            {
                return this;
            }            
        }

        protected void OnValueChanged(T oldVal, T newVal)
        {
            OnValueChanged(new ValueChangedEventArgs(oldVal, newVal));
        }

        protected void OnValueChanged(ValueChangedEventArgs e)
        {
            var handler = ValueChanged;
            if (handler != null)
            {
                handler.Invoke(this, e);
            }
        }

        public virtual bool CanBeConstant { get { return false; } }

        protected abstract T GetValue();

        public bool IsAttached { get; private set; }

        public void Detach()
        {
            if (IsAttached)
            {
                IsAttached = false;
                DetachCore();
            }
        }

        public void Attach()
        {
            if (!IsAttached)
            {
                AttachCore();
                IsAttached = true;
                Refresh();
            }
        }

        protected abstract void DetachCore();
        protected abstract void AttachCore();


        public abstract bool IsParameterFree
        {
            get;
        }

        public abstract IObservableExpression<T> ApplyParameters(IDictionary<string, object> parameters);


        public virtual bool IsConstant
        {
            get { return false; }
        }
    }

    internal interface IObservableExpression<out T> : INotifyValue<T>
    {
        bool CanBeConstant { get; }

        bool IsConstant { get; }

        bool IsParameterFree { get; }

        IObservableExpression<T> ApplyParameters(IDictionary<string, object> parameters);

        void Refresh();

        IObservableExpression<T> Reduce();
    }

    internal interface IObservableReversableExpression<T> : IObservableExpression<T>, INotifyReversableValue<T>
    {
    }
}
